widget: Redo how gtk_widget_queue_draw() works
authorBenjamin Otte <otte@redhat.com>
Tue, 18 Oct 2016 17:43:32 +0000 (19:43 +0200)
committerBenjamin Otte <otte@redhat.com>
Thu, 27 Oct 2016 03:07:23 +0000 (05:07 +0200)
Before, we would immediately invalidate the GdkWindow of the widget, now
we call the parent's GtkWidgetClass.queue_draw_child() function.
This allows the parent to track redraw queueing of children.

By default GtkWidgetClass.queue_draw_child() will again chain up to its
parent while respecting the GdkWindow hierarchy for clipping.
GtkWindow is then the only widget actually invalidating the GdkWindow.

This essentially moves redraw queueing from GDK to GTK.

gtk/gtkwidget.c
gtk/gtkwidget.h
gtk/gtkwindow.c

index 3ec848d30a3ca3e9f79b9b0854097857e589bcc9..8613e9c01dc2d21c7129308eca8d9f0a231f2200 100644 (file)
@@ -705,6 +705,9 @@ static void             gtk_widget_real_state_flags_changed     (GtkWidget
                                                                  GtkStateFlags     old_state);
 static void             gtk_widget_real_queue_draw_region       (GtkWidget         *widget,
                                                                 const cairo_region_t *region);
+static void             gtk_widget_real_queue_draw_child        (GtkWidget         *widget,
+                                                                 GtkWidget         *child,
+                                                                const cairo_region_t *region);
 static AtkObject*      gtk_widget_real_get_accessible          (GtkWidget        *widget);
 static void            gtk_widget_accessible_interface_init    (AtkImplementorIface *iface);
 static AtkObject*      gtk_widget_ref_accessible               (AtkImplementor *implementor);
@@ -1067,6 +1070,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->adjust_size_allocation = gtk_widget_real_adjust_size_allocation;
   klass->adjust_baseline_allocation = gtk_widget_real_adjust_baseline_allocation;
   klass->queue_draw_region = gtk_widget_real_queue_draw_region;
+  klass->queue_draw_child = gtk_widget_real_queue_draw_child;
 
   widget_props[PROP_NAME] =
       g_param_spec_string ("name",
@@ -5023,12 +5027,58 @@ gtk_widget_unrealize (GtkWidget *widget)
  *****************************************/
 
 static void
-gtk_widget_real_queue_draw_region (GtkWidget         *widget,
+gtk_widget_real_queue_draw_child (GtkWidget            *widget,
+                                  GtkWidget            *child,
+                                 const cairo_region_t *child_region)
+{
+  GdkWindow *child_window, *window;
+  cairo_region_t *region;
+
+  window = gtk_widget_get_window (widget);
+  child_window = gtk_widget_get_window (child);
+
+  if (child_window == window)
+    gtk_widget_queue_draw_region (widget, child_region);
+
+  region = cairo_region_copy (child_region);
+  while (child_window != window && !cairo_region_is_empty (region))
+    {
+      int x, y;
+
+      /* clip to current window */
+      cairo_region_intersect_rectangle (region, &(GdkRectangle) { 
+                                        0, 0,
+                                        gdk_window_get_width (child_window),
+                                        gdk_window_get_height (child_window)});
+
+      /* make region relative to next window */
+      gdk_window_get_position (child_window, &x, &y);
+      cairo_region_translate (region, x, y);
+      child_window = gdk_window_get_parent (child_window);
+    }
+
+  gtk_widget_queue_draw_region (widget, region);
+  cairo_region_destroy (region);
+}
+
+static void
+gtk_widget_queue_draw_child (GtkWidget            *parent,
+                             GtkWidget            *child,
+                             const cairo_region_t *region)
+{
+  WIDGET_CLASS (parent)->queue_draw_child (parent, child, region);
+}
+
+static void
+gtk_widget_real_queue_draw_region (GtkWidget            *widget,
                                   const cairo_region_t *region)
 {
-  GtkWidgetPrivate *priv = widget->priv;
+  GtkWidget *parent = _gtk_widget_get_parent (widget);
+
+  if (parent == NULL)
+    return;
 
-  gdk_window_invalidate_region (priv->window, region, TRUE);
+  gtk_widget_queue_draw_child (parent, widget, region);
 }
 
 /**
@@ -5053,17 +5103,14 @@ void
 gtk_widget_queue_draw_region (GtkWidget            *widget,
                               const cairo_region_t *region)
 {
-  GtkWidget *w;
-
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
   if (cairo_region_is_empty (region))
     return;
 
-  /* Just return if the widget or one of its ancestors isn't mapped */
-  for (w = widget; w != NULL; w = w->priv->parent)
-    if (!_gtk_widget_get_mapped (w))
-      return;
+  /* Just return if the widget isn't mapped */
+  if (!_gtk_widget_get_mapped (widget))
+    return;
 
   WIDGET_CLASS (widget)->queue_draw_region (widget, region);
 }
index bb09211f0e13306667e0f45223c8b1e2e22ae599..8b93b858661b4ebfa172100c3a7b6583fbf9c860 100644 (file)
@@ -333,8 +333,9 @@ struct _GtkWidget
  * @adjust_baseline_request:
  * @adjust_baseline_allocation:
  * @queue_draw_region: Invalidates the area of widget defined by
- *   region by calling gdk_window_invalidate_region() on the widget's
- *   window and all its child windows.
+ *   region.
+ * @queue_draw_child: Child wants to be redrawn. The region given is in
+ *   the child's coordinate system.
  */
 struct _GtkWidgetClass
 {
@@ -555,10 +556,13 @@ struct _GtkWidgetClass
   void         (* adjust_baseline_request)(GtkWidget         *widget,
                                            gint              *minimum_baseline,
                                            gint              *natural_baseline);
-  void         (* adjust_baseline_allocation) (GtkWidget         *widget,
-                                              gint              *baseline);
-  void         (*queue_draw_region)           (GtkWidget         *widget,
-                                              const cairo_region_t *region);
+  void         (* adjust_baseline_allocation) (GtkWidget             *widget,
+                                              gint                  *baseline);
+  void         (* queue_draw_region)           (GtkWidget            *widget,
+                                               const cairo_region_t *region);
+  void         (* queue_draw_child)            (GtkWidget            *widget,
+                                                GtkWidget            *child,
+                                               const cairo_region_t *region);
 
   GskRenderNode *(* get_render_node) (GtkWidget *widget,
                                       GskRenderer *renderer);
index c95231e7c1dad6e9d0317d835381b89caa1a5080..bb4b48a361741af5c84730ae3bd0deba249c161f 100644 (file)
@@ -519,6 +519,8 @@ static void        gtk_window_do_popup         (GtkWindow      *window,
 static void gtk_window_style_updated (GtkWidget     *widget);
 static void gtk_window_state_flags_changed (GtkWidget     *widget,
                                             GtkStateFlags  previous_state);
+static void gtk_window_queue_draw_region (GtkWidget            *widget,
+                                         const cairo_region_t *region);
 
 static GSList      *toplevel_list = NULL;
 static guint        window_signals[LAST_SIGNAL] = { 0 };
@@ -776,6 +778,7 @@ gtk_window_class_init (GtkWindowClass *klass)
   widget_class->state_flags_changed = gtk_window_state_flags_changed;
   widget_class->style_updated = gtk_window_style_updated;
   widget_class->get_render_node = gtk_window_get_render_node;
+  widget_class->queue_draw_region = gtk_window_queue_draw_region;
 
   container_class->remove = gtk_window_remove;
   container_class->check_resize = gtk_window_check_resize;
@@ -8198,6 +8201,19 @@ gtk_window_style_updated (GtkWidget *widget)
     update_themed_icon (GTK_WINDOW (widget));
 }
 
+static void
+gtk_window_queue_draw_region (GtkWidget            *widget,
+                             const cairo_region_t *region)
+{
+  if (_gtk_widget_get_parent (widget))
+    {
+      GTK_WIDGET_CLASS (gtk_window_parent_class)->queue_draw_region (widget, region);
+      return;
+    }
+
+  gdk_window_invalidate_region (_gtk_widget_get_window (widget), region, TRUE);
+}
+
 /**
  * _gtk_window_unset_focus_and_default:
  * @window: a #GtkWindow